#include <ne.h>
#include <TinyXML.h>

using namespace std;
using namespace ne;

/** Sort by distance functor */
struct SortByDistanceFunctor
{
    /// Position to sort in
    Vector3 mSortPos;

    SortByDistanceFunctor(const Vector3& pos) : mSortPos(pos) {}
    float operator () (Vector3& pos) const
    {
        return -(mSortPos - pos).squaredLength();
    }
};

/** Sort by direction functor */
struct SortByDirectionFunctor
{
    /// Direction to sort in
    Vector3 mSortDir;

    SortByDirectionFunctor(const Vector3& dir) : mSortDir(dir) {}
    float operator () (Vector3& pos) const
    {
        return mSortDir.dotProduct(pos);
    }
};

int main()
{
    Vector3 v1V3;
    cout << v1V3 << endl;
    v1V3.x = 2.0f;
    v1V3.y = 2.0f;
    v1V3.z = 2.0f;
    cout << v1V3 << endl;

    Vector3 v2V3(0.2f, 0.3f, -0.5f);
    cout << v2V3 << endl;
    assert(v2V3 == Vector3(v2V3));
    assert(v1V3 > v2V3);
    assert(v2V3 < v1V3);
    cout << v1V3.length() << endl;
    cout << v1V3.squaredLength() << endl;
    cout << v1V3.distance(v2V3) << endl;
    cout << v1V3.squaredDistance(v2V3) << endl;
    Vector3 v3V3(v2V3);
    v2V3.normalize();
    printf("v2V3 %.12f, %.12f, %.12f\n", v2V3.x, v2V3.y, v2V3.z);
    printf("v3V3 %.12f, %.12f, %.12f\n", v3V3.x, v3V3.y, v3V3.z);
    Vector3 v4V3(v2V3.getUnit());
    printf("v4V3 %.12f, %.12f, %.12f\n", v4V3.x, v4V3.y, v4V3.z);
    assert(Vector3::Equal(v2V3.getUnit(), v2V3));
    assert(Vector3::Equal(v2V3.getUnit(), v2V3));
    assert(Vector3::Equal(v2V3.getUnit(), v4V3));

    v3V3 = Vector3(1.0f, 1.0f, 0.0f);
    assert(v3V3.perpendicular() == Vector3(0.0f, 0.0f, -1.0f));
    v3V3 = Vector3(1.0f, 0.0f, 0.0f);
    cout << v1V3.reflect(v3V3) << endl;
    assert(v1V3.reflect(v3V3) == Vector3(2.0f, -2.0f, -2.0f));

    Vector3 points[3];
    points[0] = Vector3(1.0f, -1.0f, 1.0f);
    points[1] = Vector3(2.0f, 0.0f, 0.0f);
    points[2] = Vector3(1.0f, 1.0f, 1.0f);
    Vector2 uvs[3];
    uvs[0] = Vector2(0.0f, 0.0f);
    uvs[1] = Vector2(1.0f, 0.5f);
    uvs[2] = Vector2(0.0f, 1.0f);
    cout << "Tangent: " << TangentSpaceCalculator::CalculateTangent(points, uvs) << endl;

    Matrix3 m1M3;
    cout << m1M3 << endl;
    m1M3.identity();
    cout << m1M3 << endl;
    m1M3.fromAxisAngle(v3V3, Math::HALF_PI);
    cout << m1M3 << endl;
    cout << v1V3 * m1M3 << endl;
    m1M3.fromAxisXAngle(Math::HALF_PI);
    cout << m1M3 << endl;
    cout << v1V3 * m1M3 << endl;
    cout << m1M3.adjoint() << endl;

    Quaternion q1Q(m1M3);
    cout << q1Q << endl;
    cout << q1Q * v1V3 << endl;
    Quaternion q2Q(q1Q);
    q2Q = q1Q * q2Q;
    cout << q2Q << endl;
    cout << q2Q * v1V3 << endl;
    cout << Quaternion::MakeRotation(Vector3::UNIT_X, Vector3(1.0f, 1.0f, 0.0f)) << endl;

    v1V3.normalize();
    Plane pP(v1V3, Vector3::NEGATIVE_ONE);
    cout << pP << endl;
    v3V3 = Vector3::ONE;
    cout << v3V3 << endl;
    cout << pP.getDistance(v3V3) << endl;
    cout << pP.projectVector(v3V3) << endl;
    cout << pP.reflectVector(v3V3) << endl;
    m1M3.fromOrthoProjectionNormal(pP.normal);
    cout << m1M3 << endl;
    cout << v3V3 * m1M3 << endl;
    m1M3.fromReflectionNormal(pP.normal);
    cout << m1M3 << endl;
    cout << v3V3 * m1M3 << endl;

    Ray rR;
    cout << rR << endl;
    cout << rR.getPoint(2.0f) << endl;

    Frustum frustum;
    assert(frustum.intersects(Vector3::ZERO));
    assert(frustum.intersects(Vector3(0.3f, 0.4f, -10.0f)));
    assert(!frustum.intersects(Vector3(0.0f, -2.0f, -999.0f)));

    Random random;
    random.setTimeSeed();
    for (int i=0; i<10; ++i)
    {
        for (int j=0; j<10; ++j)
        {
            cout << random.randInt(-9, -100) << "\t";
        }
        cout << endl;
    }

    ne::real res = 0.5f;
    while (res != 1.0f)
    {
        res = Random::RandReal();
        if (res < 1e-3)
        {
            cout << res << "\t";
        }
    }

    res = 0.5f;
    while (res != 1.0f)
    {
        res = random.randReal();
        if (res < 1e-3)
        {
            cout << res << "\t";
        }
    }
    cout << endl;

    // Test Any
    Any any1(1.02f);
    cout << "Any1 = " << any_cast<float>(any1) << endl;
    Any any2 = any1;
    cout << "Any2 = " << any_cast<float>(any2) << endl;
    Any any3(any2);
    cout << "Any3 = " << any_cast<ne::real>(any3) << endl;
    Any anyVector;
    anyVector = v1V3;
    cout << "AnyVector = " << any_cast<Vector3>(anyVector) << endl;
    any3 = anyVector;
    cout << "Any3 = " << any_cast<Vector3>(any3) << endl;
    assert(any_cast<Vector3>(any3) == v1V3);
    cout << "Any3 = " << any3.value<Vector3>() << endl;

    // Test Exception
    try
    {
        throw Exception();
    }
    catch (exception &e)
    {
        std::cout << e.what() << endl;
    }

    // Test SharedPtr
    float *ptr = new float(1.01f);
    SharedPtr<float> *sPtr1 = new SharedPtr<float>(ptr);
    cout << "ptr = " << **sPtr1 << " UseCount[" << (*sPtr1).getUseCount() << "]\n";
    SharedPtr<float> *sPtr2 = new SharedPtr<float>(*sPtr1);
    cout << "ptr = " << **sPtr2 << " UseCount[" << (*sPtr1).getUseCount() << "]\n";
    SharedPtr<float> sPtr3 = *sPtr1;
    cout << "ptr = " << *sPtr3 << " UseCount[" << (*sPtr1).getUseCount() << "]\n";
    SharedPtr<float> sPtr4;
    sPtr4 = *sPtr1;
    cout << "ptr = " << *sPtr4 << " UseCount[" << (*sPtr1).getUseCount() << "]\n";
    *sPtr4 += 2.0f;
    cout << "ptr = " << **sPtr1 << "\n";
    delete sPtr2;
    delete sPtr1;

    RadixSort32<std::list<Vector3>, Vector3, float> radixSorter;
    std::list<Vector3> vectorList;
    std::list<Vector3>::iterator it, iend;
    cout << "Sort Vector3 ======\n";
    for (int i=0; i<10; ++i)
    {
        Vector3 v(Random::RandReal(-1.0, 1.0f), Random::RandReal(-1.0, 1.0f), Random::RandReal(-1.0, 1.0f));
        vectorList.push_back(v);
        cout << "[" << i << "]" << v << endl;
    }
    radixSorter.sort(vectorList, SortByDistanceFunctor(Vector3::ZERO));
    cout << "Distance sort ======\n";
    it = vectorList.begin();
    iend = vectorList.end();
    for (int i=0; it!=iend; ++i, ++it)
    {
        cout << "[" << i << "]" << *it << " " << SortByDistanceFunctor(Vector3::ZERO).operator()(*it) << endl;
    }

    radixSorter.sort(vectorList, SortByDirectionFunctor(Vector3::UNIT_Z));
    cout << "Direction sort ======\n";
    it = vectorList.begin();
    iend = vectorList.end();
    for (int i=0; it!=iend; ++i, ++it)
    {
        cout << "[" << i << "]" << *it << " " << SortByDirectionFunctor(Vector3::UNIT_Z).operator()(*it) << endl;
    }

    // Test TinyXML
    cout << "Test TinyXML ======\n";
    const char *xmlStr = "<doc attr0='1' attr1='2.121' attr2='Nuclear Engine *^_^*' attr3='true'/>";
    XMLDocument doc;
    doc.Parse(xmlStr);
    XMLElement *ele = doc.FirstChildElement();
    int result = 0;
    int iVal = 0;
    double dVal = 0.0f;
    ne::real rVal = 0.0f;
    bool flag = false;
    string text;
    result = ele->QueryDoubleAttribute( "attr0", &dVal );
    cout << "Query attribute0: int as double [" << dVal << "]\n";
    result = ele->QueryIntAttribute( "attr0", &iVal );
    cout << "Query attribute0: int as int [" << iVal << "]\n";
    result = ele->QueryDoubleAttribute( "attr1", &dVal );
    cout << "Query attribute1: double as double [" << dVal << "]\n";
    result = ele->QueryIntAttribute( "attr1", &iVal );
    cout << "Query attribute1: double as int [" << iVal << "]\n";
    result = ele->QueryFloatAttribute( "attr1", &rVal );
    cout << "Query attribute1: double as real [" << rVal << "]\n";
    result = ele->QueryStringAttribute( "attr2", &text );
    cout << "Query attribute2: string as string [" << text << "]\n";
    result = ele->QueryBoolAttribute( "attr3", &flag );
    cout << "Query attribute2: bool as bool [" << (flag ? "True" : "False") << "]\n";

    // Test Unicode
    cout << "Unicode Test ======\n";
    string str("我");
    cout << "UTF8 : ";
    for (ne::size_t i=0; i<str.length(); ++i)
    {
        printf("0x%08x ", str[i]);
    }
    cout << endl;

    Unicode uStr(str);
    cout << "UTF32 : ";
    printf("0x%08x\n", uStr[0]);

    assert(str == uStr.toUTF8());

    str = uStr.toUTF8();
    cout << "UTF8 : ";
    for (ne::size_t i=0; i<str.length(); ++i)
    {
        printf("0x%08x ", str[i]);
    }
    cout << endl;

    uStr.clear();
    uStr.append(0x6211);
    cout << "UTF32 : ";
    printf("0x%08x\n", uStr[0]);

    assert(str == uStr.toUTF8());

    str = uStr.toUTF8();
    cout << "UTF8 : ";
    for (ne::size_t i=0; i<str.length(); ++i)
    {
        printf("0x%08x ", str[i]);
    }
    cout << endl;

    uStr.fromUTF8("翔");
    cout << "UTF32 : ";
    printf("0x%08x\n", uStr[0]);

    assert(uStr[0] == 0x7FD4);

    str = "\xE7\xBF\x94";
    assert(str == uStr.toUTF8());

    str = uStr.toUTF8();
    cout << "UTF8 : ";
    for (ne::size_t i=0; i<str.length(); ++i)
    {
        printf("0x%08x ", str[i]);
    }
    cout << endl;
    assert((ne::uint8_t(str[0]) == 0xE7) && (ne::uint8_t(str[1]) == 0xBF) && (ne::uint8_t(str[2]) == 0x94));

    uStr = "Rockie";
    uStr += "翔";
    uStr += 0x32;

    return 0;
}
